package ner;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class NERecognizer {

	
	
	
	public Set<BookCharacter> performInitialNER(ExtractionTask extractionTask, Segmenter segmenter) {
		
		int sIndex=0;
		int counter=0;
		Map<String,BookCharacter> nes=new HashMap<String,BookCharacter>();
		for(String sentence:extractionTask.getSentences()){
			
			List<String> POStags=extractionTask.getPOStags().get(sIndex);
			List<String> baseWords=extractionTask.getBaseWords().get(sIndex);
			List<String> words=Segmenter.splitByWords(sentence);
			int wIndex=0;
			String prevWord=null;
			
			for(String w:words){
	
				if(baseWords.get(wIndex).equals(POStagger.UNKNOWN_TAG) && isNECandidate(w)){
					boolean precededByTitle=false;
					String title=null;
					
					if(wIndex>0){
						
						if((Knowledge.titlesBeforePeopleNames.contains(baseWords.get(wIndex-1)))){
							precededByTitle=true;
							title=baseWords.get(wIndex-1);
						}
						else if(Knowledge.titlesBeforePeopleNames.contains(prevWord.toLowerCase().trim())){
							precededByTitle=true;
							title=prevWord.toLowerCase().trim();
						}
					}
					
					if(nes.containsKey(w)){
						BookCharacter existing=nes.get(w);
						existing.increaseOccurrences();
						existing.getSentencesNumbers().add(sIndex);
						
						if(existing.certainity.foundUppercasedInTheMiddle==false && wIndex>0)
							existing.certainity.foundUppercasedInTheMiddle=true;
						if(precededByTitle){
							existing.certainity.precededByTitle+=1;		//maybe delete this field if you have list
							Integer value=null;
							if((value=existing.getTitles().get(title))!=null){
								existing.getTitles().put(title,value+1);
							}
							else{
								existing.getTitles().put(title,1);
							}
							
						}
					}	
					else{
						BookCharacter newCh=new BookCharacter(w,(wIndex>0));
						newCh.getSentencesNumbers().add(sIndex);
						
						if(precededByTitle){
							newCh.certainity.precededByTitle+=1;
							newCh.getTitles().put(title, 1);
						}
						nes.put(w,newCh);
						
						
					}
						
				}
				wIndex++;
				prevWord=w;
			
			}
			
			
			sIndex++;
		}

		markEntitiesKnownAsOtherNEType(nes);
		
	
		Map<String,BookCharacter> mergedCharacters=mergeDiffrentGrammaticalCases(nes);
		
		
		
		
		Set<BookCharacter> result=new HashSet<BookCharacter>();

		for(BookCharacter ch:mergedCharacters.values()){
			result.add(ch);
		//	if(ch.getOccurrencesNumber()>30 && ch.certainity.foundAsOtherNEType==false)
		//		System.out.println(ch);
		}
		
		
		return result;
		
	}
	
	private void markEntitiesKnownAsOtherNEType(Map<String,BookCharacter> nes){
		
		Set<String> foundAsOtherNEType=Util.caseInsensitiveIntersection(nes.keySet(), Knowledge.geoNames);
		foundAsOtherNEType.addAll(Util.caseInsensitiveIntersection(nes.keySet(), Knowledge.countriesAndCapitals));
	
		for(String ne:foundAsOtherNEType){
			nes.get(ne).certainity.foundAsOtherNEType=true;
		}

		
	}

	private boolean isNECandidate(String word) {
		//moze jakies punkty?no bo np moze miec wielka litere ale bedac na poczatku zdania, odrzucic nie, wziac bezkrytycznie tez nie
		if(word.length()<2)
			return false;
		if(java.lang.Character.isUpperCase(word.charAt(0))==false)
			return false;	
		return true;
	
	}
	
	private Map<String,BookCharacter> mergeDiffrentGrammaticalCases(Map<String,BookCharacter> nes) {
		
		String[] names=new String[nes.size()];
		boolean[] taken=new boolean[nes.size()];
		int i=0;
		for(String name:nes.keySet()){
			names[i]=name;
			taken[i]=false;
			i++;
		}

		List<Set<String>> classes=new ArrayList<Set<String>>();
		
		for(int k=0;k<names.length;k++){
			if(taken[k]==false){
				Set<String> newSet=new HashSet<String>();
				newSet.add(names[k]);
				visit(k,names,taken,newSet);
				classes.add(newSet);
			}		
		}
		
		Map<String,BookCharacter> mergedCharacters= new HashMap<String,BookCharacter>();
		int counter=0;
	//	System.out.println("CLASSES\n");
		for(Set<String> set:classes){
		//	System.out.println("--SET--");
			BookCharacter ch=mergeCharactersInSetToOne(set,nes);
			mergedCharacters.put(ch.getName(),ch);
			for(String s:set){
				
			//	System.out.println(s);
				
				counter++;
			}
			
		}
	
		System.out.println("ORIGINAL SIZE"+nes.size());
		System.out.println("CLASSES SIZE"+counter);
		
		return mergedCharacters;
		
	
	}
	

	private BookCharacter mergeCharactersInSetToOne(Set<String> set,			//TODO double check!maybe move to BookCharacter
			Map<String, BookCharacter> nes) {
		
		int maxOcc=0;
		int occInTotal=0;	
		List<Integer> sentenceNumbers=new ArrayList<Integer>();
		String mostPopularName=null;
		BookCharacter.Certainity certainity=new BookCharacter.Certainity();
		Map<String,Integer> titles=new HashMap<String,Integer>();
		List<String> shortestNames=new ArrayList<String>();
		
		for(String name:set){
			if(shortestNames.size()==0)
				shortestNames.add(name);
			BookCharacter  bch=nes.get(name);
			int occ=bch.getOccurrencesNumber();
			if(occ>maxOcc){
				maxOcc=occ;
				mostPopularName=name;
			}
			if(name.length()==shortestNames.get(0).length())
				shortestNames.add(name);
			if(name.length()<shortestNames.get(0).length()){
				shortestNames.clear();
				shortestNames.add(name);
			
			}
			occInTotal+=occ;
			if(bch.certainity.foundUppercasedInTheMiddle){
				certainity.foundUppercasedInTheMiddle=true;	
			}
			if(bch.certainity.foundAsOtherNEType){
				certainity.foundAsOtherNEType=true;			
			}
			sentenceNumbers.addAll(bch.getSentencesNumbers());
			certainity.precededByTitle+=bch.certainity.precededByTitle;
			
			titles=Util.addMapsWithOccurrences(titles,bch.getTitles() );
		}
		Set<String> aliases=new HashSet<String>(set);
	//	aliases.remove(mostPopularName);
		String selectedName=shortestNames.get(0);
		if(shortestNames.size()>0){
			int maxOccAmongShortest=nes.get(shortestNames.get(0)).getOccurrencesNumber();
			
			for(String name:shortestNames){
				if(maxOccAmongShortest<nes.get(name).getOccurrencesNumber()){
					maxOccAmongShortest=nes.get(name).getOccurrencesNumber();
					selectedName=name;
				}
			}
		}
		BookCharacter representingCh=new BookCharacter(selectedName,aliases,certainity,occInTotal);
		representingCh.setSentencesNumbers(sentenceNumbers);
		representingCh.setTitles(titles);
		return representingCh;
		
	}
	
	


	void visit(int currIndex,String[] names,boolean[] taken, Set<String> result){
		
		for(int j=currIndex+1;j<names.length;j++){
				if(taken[j]==false){
					if(Util.getWordDistance(names[currIndex], names[j])<2){
						taken[j]=true;
						result.add(names[j]);
						visit(j,names,taken,result);
					}
				}
				
		}		
	
		
	}
	
	
	
	
}
